package net.phoenix.repository;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.jcr.AccessDeniedException;
import javax.jcr.Binary;
import javax.jcr.InvalidItemStateException;
import javax.jcr.Item;
import javax.jcr.ItemExistsException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.ItemVisitor;
import javax.jcr.NoSuchWorkspaceException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeIterator;
import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.nodetype.PropertyDefinition;
import javax.jcr.observation.Event;
import javax.jcr.version.VersionException;

import net.phoenix.repository.dao.MaxNodeIndexDao;
import net.phoenix.repository.dao.NodeTableCountDao;
import net.phoenix.repository.dao.NodeTableDeleteDao;
import net.phoenix.repository.dao.NodeTableInsertDao;
import net.phoenix.repository.dao.NodeTableUpdateDao;
import net.phoenix.repository.dao.ValueTableDeleteDao;
import net.phoenix.repository.dao.ValueTableInsertDao;
import net.phoenix.repository.dao.ValueTableQueryDao;
import net.phoenix.repository.dao.ValueTableUpdateDao;
import net.phoenix.repository.data.NodeData;
import net.phoenix.repository.iterator.BasicPropertyIterator;
import net.phoenix.repository.iterator.ChildNodeIterator;
import net.phoenix.repository.iterator.FollowingSiblingNodeIterator;
import net.phoenix.repository.iterator.PrecedingSiblingNodeIterator;
import net.phoenix.repository.observation.BasicObservationManager;
import net.phoenix.repository.property.BasicProperty;
import net.phoenix.repository.query.xpath.XPathQuery;
import net.phoenix.repository.utils.AntPathMatcher;

/**
 * 
 * Jigsaw server
 * 
 * @author <a href="shamphone@gmail.com">Li XiongFeng</a>
 * @date 2012-1-2
 * @version 1.0.0
 */
public class BasicNode extends BaseLevel1Node implements Serializable {

	private static final long serialVersionUID = -5166321459690548238L;
	// private static Log log = LogFactory.getLog(BasicNode.class);
	private JdbcSession session;
	private BasicRepository repository;
	private NodeTypeManager manager;
	private Map<String, Property> properties;
	private boolean modified;
	private NodeType primary;
	private Map<String, NodeType> mixins;
	private NodeData data;

	public BasicNode(JdbcSession session, NodeData data)
			throws RepositoryException {
		this.data = data;
		this.session = session;
		this.repository = (BasicRepository) session.getRepository();
		this.modified = true;
		this.mixins = Collections
				.synchronizedMap(new LinkedHashMap<String, NodeType>());
		this.manager = this.session.getWorkspace().getNodeTypeManager();
		this.properties = Collections
				.synchronizedMap(new HashMap<String, Property>());
		this.primary = manager.getNodeType(data.getPrimaryType());
		for (String nodeTypeName : data.getMixinTypes()) {
			NodeType type = manager.getNodeType(nodeTypeName);
			mixins.put(nodeTypeName, type);
		}
		if (!this.data.isNew())
			this.populate();
	}

	/**
	 * create a blank node;
	 * 
	 * @throws RepositoryException
	 */
	private void create() throws RepositoryException {
		this.createProperties(primary);
		for (NodeType type : this.mixins.values()) {
			this.createProperties(type);
		}
		this.session.addPending(this);
	}

	/**
	 * 
	 * @param type
	 * @throws RepositoryException
	 */
	private void createProperties(NodeType type) throws RepositoryException {
		for (PropertyDefinition definition : type.getPropertyDefinitions()) {
			BasicProperty property = BasicProperty
					.getInstance(this, definition);
			this.properties.put(property.getName(), property);
		}

		for (NodeDefinition child : type.getChildNodeDefinitions()) {
			if (child.isAutoCreated())
				this.addNode(child.getName(), child.getDefaultPrimaryTypeName());
		}

	}

	/**
	 * populate with database data;
	 * 
	 * @param dao
	 * @throws SQLException
	 * @throws RepositoryException
	 */
	protected void populate() throws RepositoryException {
		ValueTableQueryDao dao = session.getDao(ValueTableQueryDao.class);
		try {
			BasicNode.this.popularProperties(dao, primary);
			for (NodeType type : BasicNode.this.mixins.values()) {
				this.popularProperties(dao, type);
			}
		} catch (SQLException ex) {
			throw new RepositoryException(ex);
		}
	}

	/**
	 * populate with database data;
	 * 
	 * @param dao
	 * @throws SQLException
	 * @throws RepositoryException
	 */
	private void popularProperties(ValueTableQueryDao dao, NodeType type)
			throws SQLException, RepositoryException {
		this.modified = false;
		if (dao.findByID(type.getName(), this.data.getID()) == 1)
			for (PropertyDefinition propertyDef : primary
					.getPropertyDefinitions()) {
				BasicProperty property;
				String propertyName = propertyDef.getName();
				if (propertyDef.isMultiple()) {
					Value[] values = dao.getArrayValue(propertyName,
							propertyDef.getRequiredType());
					property = BasicProperty.getInstance(this, propertyDef,
							values);
				} else {
					Value value = dao.getSingleValue(propertyName,
							propertyDef.getRequiredType());
					property = BasicProperty.getInstance(this, propertyDef,
							value);
				}

				this.properties.put(propertyName, property);
			}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jcr.Item#getName()
	 */
	@Override
	public String getName() {
		return this.data.getName();
	}

	/**
	 * @return
	 */
	public String getID() {
		return this.getIdentifier();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jcr.Node#getIdentifier()
	 */
	@Override
	public String getIdentifier() {
		return this.data.getID();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jcr.Node#addNode(java.lang.String)
	 */
	@Override
	public Node addNode(String name) throws ItemExistsException,
			PathNotFoundException, VersionException,
			ConstraintViolationException, LockException, RepositoryException {
		NodeType type = JCRUtils.getChildNodeType(this.getPrimaryNodeType(),
				name);
		if (type != null)
			return this.internalAddNode(name, type.getName(), null);
		throw new PathNotFoundException("No definition with name " + name + ".");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jcr.Node#addNode(java.lang.String, java.lang.String)
	 */
	@Override
	public Node addNode(String name, String type) throws RepositoryException {
		return this.internalAddNode(name, type, null);
	}

	/**
	 * @param name
	 * @param nodeType
	 * @param id
	 * @return
	 * @throws RepositoryException
	 */
	private Node internalAddNode(String name, String nodeType, String id)
			throws RepositoryException {
		if ((id != null) && this.session.getNodeByIdentifier(id) != null)
			throw new ItemExistsException("Node with id " + id
					+ " has exists. ");
		NodeData data = new NodeData();
		if (id != null)
			data.setID(id);
		data.setName(name);
		data.setParentID(this.getID());
		data.setOrderNo(this.getMaxChildIndexNo(name) + 1);
		data.setPrimaryType(nodeType);
		BasicNode newNode = new BasicNode(this.session, data);
		newNode.create();
		return newNode;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jcr.Node#getPrimaryNodeType()
	 */
	@Override
	public NodeType getPrimaryNodeType() throws RepositoryException {
		return this.primary;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jcr.Node#setPrimaryType(java.lang.String)
	 */
	@Override
	public void setPrimaryType(String type) throws RepositoryException {
		this.primary = this.session.getWorkspace().getNodeTypeManager()
				.getNodeType(type);
		data.setPrimaryType(type);

		for (PropertyDefinition definition : primary.getPropertyDefinitions()) {
			if (this.properties.get(definition.getName()) == null) {
				BasicProperty property = BasicProperty.getInstance(this,
						definition);
				this.properties.put(property.getName(), property);
			}
		}
		if (!data.isNew()) {
			this.updateProperties(primary);
			ValueTableDeleteDao delete = session
					.getDao(ValueTableDeleteDao.class);
			try {
				delete.delete(primary.getName(), data.getID());
			} catch (SQLException e) {
				throw new RepositoryException(e);
			}

		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jcr.Item#getParent()
	 */
	@Override
	public Node getParent() throws RepositoryException {
		if (this.data.getParentID() == null)
			return null;
		try {
			return this.session.getNodeByIdentifier(this.data.getParentID());
		} catch (ItemNotFoundException ex) {
			return null;
		}
	}

	/**
	 * 设置父结点
	 * 
	 * @return Node
	 * @throws RepositoryException
	 */
	protected void setParent(Node parent, String name)
			throws RepositoryException {
		if (parent.equals(this.getParent()))
			return;
		this.data.setParentID(parent.getIdentifier());
		this.data.setOrderNo(((BasicNode) parent).getMaxChildIndexNo(name) + 1);
		this.data.setName(name);
		this.modified = true;
		this.session.addPending(this);
	}

	/**
	 * 保存nodedata的orderno数据
	 */
	@Override
	public synchronized void save() throws NoSuchWorkspaceException,
			AccessDeniedException, LockException, InvalidItemStateException,
			RepositoryException {
		final BasicObservationManager manager = (BasicObservationManager) this.session
				.getWorkspace().getObservationManager();
		if (data.isNew())
			this.insert();
		else
			this.update();
		manager.fire(BasicNode.this, Event.NODE_ADDED);

		this.modified = false;
		this.session.removePending(this);
	}

	/**
	 * 
	 * @param provider
	 * @throws SQLException
	 * @throws RepositoryException
	 * @throws IOException
	 */
	private void insert() throws RepositoryException {
		this.insertData();
		this.insertProperties(primary);
		for (NodeType mixin : this.mixins.values()) {
			this.insertProperties(mixin);
		}
	}

	/**
	 * insert node data;
	 * 
	 * @param provider
	 * @throws SQLException
	 * @throws RepositoryException
	 * @throws IOException
	 */
	private void insertData() throws RepositoryException {
		NodeTableInsertDao dao = session.getDao(NodeTableInsertDao.class);
		try {
			dao.insert(data);
		} catch (SQLException e) {
			throw new RepositoryException(e);
		}
	}

	/**
	 * 
	 * @param provider
	 * @throws SQLException
	 * @throws RepositoryException
	 * @throws IOException
	 */
	private void insertProperties(NodeType type) throws RepositoryException {
		ValueTableInsertDao dao = session.getDao(ValueTableInsertDao.class);
		for (PropertyDefinition definition : type.getPropertyDefinitions()) {
			Property property = this.properties.get(definition.getName());
			if (property == null)
				dao.removeValue(definition.getName(), definition.isMultiple());
			if (definition.isMultiple())
				dao.setColumnValues(definition.getName(), property.getValues(),
						property.getType());
			else
				dao.setColumnValue(definition.getName(), property.getValue(),
						property.getType());
		}
		try {
			dao.insert(type.getName(), data.getID());
		} catch (SQLException e) {
			throw new RepositoryException(e);
		}
	}

	/**
	 * 保存nodedata的orderno数据
	 * 
	 * @throws SQLException
	 *             `
	 * @throws IOException
	 * @throws RepositoryException
	 */
	private synchronized void update() throws RepositoryException {
		this.updateData();
		this.updateProperties(this.primary);
		for (NodeType mixin : this.mixins.values()) {
			this.updateProperties(mixin);
		}
	}

	/**
	 * 
	 * @param provider
	 * @throws SQLException
	 * @throws RepositoryException
	 * @throws IOException
	 */
	private void updateData() throws RepositoryException {
		NodeTableUpdateDao dao = session.getDao(NodeTableUpdateDao.class);
		try {
			dao.update(data);
		} catch (SQLException e) {
			throw new RepositoryException(e);
		}
	}

	/**
	 * insert mixin data to database;
	 * 
	 * @param provider
	 * @param type
	 * @throws SQLException
	 * @throws RepositoryException
	 * @throws IOException
	 */
	private void updateProperties(NodeType type) throws RepositoryException {
		if (type.getPropertyDefinitions().length == 0)
			return;
		ValueTableQueryDao query = session.getDao(ValueTableQueryDao.class);
		try {
			if (query.findByID(type.getName(), this.data.getID()) > 0) {
				ValueTableUpdateDao dao = session
						.getDao(ValueTableUpdateDao.class);
				for (PropertyDefinition definition : type
						.getPropertyDefinitions()) {
					Property property = properties.get(definition.getName());
					if (property == null)
						dao.removeValue(definition.getName(),
								definition.isMultiple());
					else {
						if (property.isModified()) {
							if (definition.isMultiple())
								dao.setColumnValues(definition.getName(),
										property.getValues(),
										property.getType());
							else
								dao.setColumnValue(definition.getName(),
										property.getValue(), property.getType());
						}
					}
				}
				dao.update(type.getName(), this.data.getID());
			} else
				this.insertProperties(type);
			query.close();
		} catch (SQLException ex) {
			throw new RepositoryException(ex);
		}
	}

	/**
	 * 获取所有的子节点
	 * 
	 * @return NodeIterator
	 */
	@Override
	public NodeIterator getNodes() {
		return new ChildNodeIterator(this.session, this.data.getID());
	}

	@Override
	public NodeIterator getFollowingSibling() {
		return new FollowingSiblingNodeIterator(this.session,
				this.data.getParentID(), this.data.getName(),
				this.data.getOrderNo());
	}

	@Override
	public NodeIterator getPrecedingSibling() {
		return new PrecedingSiblingNodeIterator(this.session,
				this.data.getParentID(), this.data.getName(),
				this.data.getOrderNo());
	}

	/**
	 * 
	 * @param name
	 *            String
	 * @return NodeIterator
	 */
	@Override
	public NodeIterator getNodes(String relPath) throws RepositoryException {
		XPathQuery path = new XPathQuery(this, relPath);
		return path.execute().getNodes();

	}

	/**
	 * 获取所有引用这个节点的其他节点
	 * 
	 * @return PropertyIterator
	 */
	@Override
	public PropertyIterator getReferences() {
		throw new UnsupportedOperationException();
	}

	/**
	 * 获得该节点在其兄弟节点的序号
	 * 
	 * @return int
	 */
	@Override
	public int getIndex() {
		return this.data.getOrderNo();
	}

	/**
	 * 设置该节点在其兄弟节点的序号
	 * 
	 * @param orderNo
	 *            int
	 * @throws RepositoryException
	 * @throws InvalidItemStateException
	 * @throws LockException
	 * @throws NoSuchWorkspaceException
	 * @throws AccessDeniedException
	 */
	public void setIndex(int orderNo) throws AccessDeniedException,
			NoSuchWorkspaceException, LockException, InvalidItemStateException,
			RepositoryException {
		this.data.setOrderNo(orderNo);
		this.modified = true;
		this.session.addPending(this);
	}

	/**
	 * parentID和name组合下的最大序号
	 * 
	 * @param name
	 *            String
	 * @return int
	 * @throws RepositoryException
	 */
	private int getMaxChildIndexNo(final String name)
			throws RepositoryException {
		final NodeType childType = this.getChildNodeType(name);
		if (childType == null)
			return 0;

		int max = -1;
		MaxNodeIndexDao dao = session.getDao(MaxNodeIndexDao.class);
		try {
			max = dao.query(childType.getName(), data.getID(), name);
			for (NodeTypeIterator iterator = childType.getSubtypes(); iterator
					.hasNext();) {
				int index = dao.query(iterator.nextNodeType().getName(),
						data.getID(), name);
				if (index > max)
					max = index;
			}
			return max;
		} catch (SQLException ex) {
			throw new RepositoryException(ex);
		}

	}

	/**
	 * Get child node type.
	 * 
	 * @param name
	 * @return
	 */
	private NodeType getChildNodeType(String name) {
		NodeType child = JCRUtils.getChildNodeType(this.primary, name);
		if (child != null)
			return child;
		for (NodeType mixin : this.mixins.values()) {
			child = JCRUtils.getChildNodeType(mixin, name);
			if (child != null)
				return child;
		}
		return null;
	}

	@Override
	public Node getNode(String relPath) throws RepositoryException {
		XPathQuery path = new XPathQuery(this, relPath);
		NodeIterator nodes = path.execute().getNodes();
		if (nodes.hasNext())
			return nodes.nextNode();
		throw new PathNotFoundException(relPath);
	}

	/**
	 * 
	 * @return Repository
	 */

	public Repository getRepository() {
		return this.repository;
	}

	/**
	 * 
	 * @param relPath
	 *            String
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 * @throws ValueFormatException
	 * @throws ItemNotFoundException
	 */
	@Override
	public Property getProperty(String relPath) throws ItemNotFoundException,
			ValueFormatException, PathNotFoundException, RepositoryException {
		Name name = this.session.parseName(relPath);
		Property property = this.properties.get(name.getQName());
		if (property != null)
			return property;
		throw new ItemNotFoundException("No property with path '" + relPath
				+ "' found in " + this.data.getID() + ".");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jcr.Node#getProperties()
	 */
	@Override
	public PropertyIterator getProperties() throws RepositoryException {
		return new BasicPropertyIterator(this.properties.values());
	}

	/**
	 * 判断在指定路径下是否有节点存在
	 * 
	 * @param path
	 *            String 子节点的相对或者绝对路径
	 * @return boolean
	 * @throws RepositoryException
	 * @throws
	 */
	@Override
	public boolean hasNode(String relPath) throws RepositoryException {
		XPathQuery path = new XPathQuery(this, relPath);
		return path.execute().getRows().getSize() > 0;
	}

	/**
	 * 判断在指定路径下是否有属性存在
	 * 
	 * @param path
	 *            String 子节点的相对或者绝对路径
	 * @return boolean
	 */
	@Override
	public boolean hasProperty(String path) throws RepositoryException {
		Name name = this.session.parseName(path);
		String qname = name.getQName();
		BasicProperty property = (BasicProperty) this.properties.get(qname);
		if (property != null)
			return property.exists();

		return false;

	}

	/**
	 * 获取附加的格式定义，一个内容可以有多个格式定义
	 * 
	 * @return NodeType[]
	 * @throws RepositoryException
	 */
	@Override
	public NodeType[] getMixinNodeTypes() throws RepositoryException {
		return this.mixins.values().toArray(new NodeType[this.mixins.size()]);
	}

	/**
	 * 判断是否是nodeDefinition类型,需要判断是否是继承关系
	 * 
	 * @param nodeType
	 *            String
	 * @return boolean
	 * @throws RepositoryException
	 */
	@Override
	public boolean isNodeType(String nodeType) throws RepositoryException {
		if (this.getPrimaryNodeType() == null)
			return false;
		if (nodeType == null)
			throw new NullPointerException(" nodeType should not be null.");
		if (this.getPrimaryNodeType().isNodeType(nodeType))
			return true;
		NodeType[] types = this.getMixinNodeTypes();
		for (int i = 0; i < types.length; i++)
			if (types[i].isNodeType(nodeType))
				return true;
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jcr.Node#addMixin(java.lang.String)
	 */
	@Override
	public void addMixin(String nodeType) throws RepositoryException {
		if (!this.canAddMixin(nodeType))
			return;
		NodeType type = this.manager.getNodeType(nodeType);
		this.mixins.put(type.getName(), type);
		Map<String, Property> properties = new LinkedHashMap<String, Property>();
		for (PropertyDefinition propertyDef : type.getPropertyDefinitions()) {
			if (properties.get(propertyDef.getName()) == null) {
				BasicProperty property = BasicProperty.getInstance(this,
						propertyDef);
				properties.put(property.getName(), property);
			}
		}
		for (NodeDefinition child : type.getChildNodeDefinitions()) {
			if (child.isAutoCreated()) {
				this.addNode(child.getName(), child.getDefaultPrimaryTypeName());
			}
		}
		this.modified = true;
		this.data.getMixinTypes().add(nodeType);
		this.session.addPending(this);
	}

	/**
	 * 删除类型，删除后，这个类型对应的属性将被删除，如果某个属性在已有的类型中还在使用，则保留
	 * 
	 * @param nodeDefinitionName
	 *            String
	 * @throws RepositoryException
	 * @throws
	 */
	public void removeMixin(final String type) throws RepositoryException {
		this.mixins.remove(type);
		this.data.getMixinTypes().remove(type);
		this.modified = true;
		this.session.addPending(this);
		if (!data.isNew()) {
			ValueTableDeleteDao dao = session.getDao(ValueTableDeleteDao.class);
			try {
				dao.delete(type, data.getID());
			} catch (SQLException e) {
				throw new RepositoryException(e);
			}
		}
	}

	/**
	 * 判断是否可以将nodeDefinitionName类型设置为这个节点的附加类型.
	 * 如果节点类型中已经包含了definition指定的类型，则不能再添加
	 * 
	 * @param nodeDefinitionName
	 *            String
	 * @return boolean
	 * @throws RepositoryException
	 */
	public boolean canAddMixin(String definition) throws RepositoryException {
		return !this.isNodeType(definition);
	}

	/**
	 * 获取本节点在父节点下的属性定义
	 * 
	 * @return ChildNodeDefinition
	 * @throws RepositoryException
	 */
	@Override
	public NodeDefinition getDefinition() throws RepositoryException {
		if (this.getName() == null)
			return null;
		AntPathMatcher matcher = new AntPathMatcher();
		Node parent = this.getParent();
		if (parent == null)
			return null;
		for (NodeDefinition definition : parent.getPrimaryNodeType()
				.getChildNodeDefinitions()) {
			if (definition.getName().equals(this.getName()))
				return definition;
		}
		for (NodeDefinition definition : parent.getPrimaryNodeType()
				.getChildNodeDefinitions()) {
			if (matcher.match(definition.getName(), this.getName()))
				return definition;
		}
		return null;
	}

	/**
	 * 获取节点路径 节点路径为： 根节点的路径为/ 当前节点路径=父节点路径+/+ 本节点的name + [当前节点的OrderNo]
	 * 如果当前节点在父节点下name是唯一的，则 [当前节点的OrderNo]在路径上可以省略 *
	 * 
	 * @return String 类似 /a/b/c[1]/d/e[10]的路径
	 * @throws RepositoryException
	 */
	public String getPath() throws RepositoryException {
		if (this.getParent() == null)
			return "/";
		if (session.getRootNode().equals(this))
			return "/";
		String path = this.getParent().getPath();
		if (!path.endsWith("/"))
			path += "/";
		NodeDefinition def = this.getDefinition();
		// 父节点没有明确定义子节点的类型,则路径为父节点路径+name;
		if (def == null)
			return path + this.getName();
		// 父节点中定义的子节点类型是唯一的；
		if (!def.allowsSameNameSiblings())
			return path + this.getName();
		// 如果根节点不唯一
		return path + this.getName() + "[" + (this.getIndex()) + "]";
	}

	/**
	 * 设置属性值。如果这个属性不存在，则创建这个属性。 属性的类型由结点定义确定，如果这个属性是其他类型，则系统作尽可能的类型转换。
	 * 如果系统未定义该属性类型，则如果属性
	 * 如果转换出错，则抛出ValueFormatException，其他错误，则抛出RepositoryException.
	 * 
	 * @param name
	 *            String
	 * @param value
	 *            Node
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 */
	public Property setProperty(String name, Node value)
			throws PathNotFoundException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		property.setValue(value);
		session.addPending(this);
		return property;
	}

	/**
	 * 设置属性值。如果这个属性不存在，则创建这个属性。 属性的类型由结点定义确定，如果这个属性是其他类型，则系统作尽可能的类型转换。
	 * 如果系统未定义该属性类型，则如果属性
	 * 如果转换出错，则抛出ValueFormatException，其他错误，则抛出RepositoryException.
	 * 
	 * @param name
	 *            String
	 * @param values
	 *            Value[]
	 * @param type
	 *            int
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 */
	public Property setProperty(String name, Value[] values, int type)
			throws PathNotFoundException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		if (type == property.getType())
			this.setProperty(name, values);
		else
			throw new ValueFormatException(
					"Can't set property value, required is "
							+ property.getType() + ", new type is " + type
							+ " .");
		session.addPending(this);
		return property;
	}

	/**
	 * 设置属性值。如果这个属性不存在，则创建这个属性。 属性的类型由结点定义确定，如果这个属性是其他类型，则系统作尽可能的类型转换。
	 * 如果系统未定义该属性类型，则如果属性
	 * 如果转换出错，则抛出ValueFormatException，其他错误，则抛出RepositoryException.
	 * 
	 * @param name
	 *            String
	 * @param value
	 *            long
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 */
	public Property setProperty(String name, long value)
			throws PathNotFoundException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		property.setValue(value);
		session.addPending(this);
		return property;

	}

	/**
	 * 设置属性值。如果这个属性不存在，则创建这个属性。 属性的类型由结点定义确定，如果这个属性是其他类型，则系统作尽可能的类型转换。
	 * 如果系统未定义该属性类型，则如果属性
	 * 如果转换出错，则抛出ValueFormatException，其他错误，则抛出RepositoryException.
	 * 
	 * @param name
	 *            String
	 * @param value
	 *            String
	 * @param type
	 *            int
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 */
	public Property setProperty(String name, String value, int type)
			throws PathNotFoundException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		Value tvalue = this.getSession().getValueFactory()
				.createValue(value, type);
		property.setValue(tvalue);
		session.addPending(this);
		return property;

	}

	/**
	 * 设置属性值。如果这个属性不存在，则创建这个属性。 属性的类型由结点定义确定，如果这个属性是其他类型，则系统作尽可能的类型转换。
	 * 如果系统未定义该属性类型，则如果属性
	 * 如果转换出错，则抛出ValueFormatException，其他错误，则抛出RepositoryException.
	 * 
	 * @param name
	 *            String
	 * @param value
	 *            Value
	 * @param type
	 *            int
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 */
	public Property setProperty(String name, Value value, int type)
			throws PathNotFoundException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		property.setValue(value);
		session.addPending(this);
		return property;

	}

	/**
	 * 设置属性值。如果这个属性不存在，则创建这个属性。 属性的类型由结点定义确定，如果这个属性是其他类型，则系统作尽可能的类型转换。
	 * 如果系统未定义该属性类型，则如果属性
	 * 如果转换出错，则抛出ValueFormatException，其他错误，则抛出RepositoryException.
	 * 
	 * @param name
	 *            String
	 * @param values
	 *            Value[]
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 */
	public Property setProperty(String name, Value[] values)
			throws PathNotFoundException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		property.setValue(values);
		session.addPending(this);
		return property;

	}

	/**
	 * 设置属性值。如果这个属性不存在，则创建这个属性。 属性的类型由结点定义确定，如果这个属性是其他类型，则系统作尽可能的类型转换。
	 * 如果系统未定义该属性类型，则如果属性
	 * 如果转换出错，则抛出ValueFormatException，其他错误，则抛出RepositoryException.
	 * 
	 * @param name
	 *            String
	 * @param values
	 *            String[]
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 */
	public Property setProperty(String name, String[] values)
			throws PathNotFoundException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		property.setValue(values);
		session.addPending(this);
		return property;

	}

	/**
	 * 设置属性值。如果这个属性不存在，则创建这个属性。 属性的类型由结点定义确定，如果这个属性是其他类型，则系统作尽可能的类型转换。
	 * 如果系统未定义该属性类型，则如果属性
	 * 如果转换出错，则抛出ValueFormatException，其他错误，则抛出RepositoryException.
	 * 
	 * @param name
	 *            String
	 * @param value
	 *            double
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 */
	public Property setProperty(String name, double value)
			throws PathNotFoundException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		property.setValue(value);
		session.addPending(this);
		return property;

	}

	@Override
	public Property setProperty(String name, Binary value)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		property.setValue(value);
		session.addPending(this);
		return property;
	}

	@Override
	public Property setProperty(String name, BigDecimal value)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * 设置属性值。如果这个属性不存在，则创建这个属性。 属性的类型由结点定义确定，如果这个属性是其他类型，则系统作尽可能的类型转换。
	 * 如果系统未定义该属性类型，则如果属性
	 * 如果转换出错，则抛出ValueFormatException，其他错误，则抛出RepositoryException.
	 * 
	 * @param name
	 *            String
	 * @param value
	 *            Value
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 */
	public Property setProperty(String name, Value value)
			throws PathNotFoundException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		property.setValue(value);
		session.addPending(this);
		return property;

	}

	/**
	 * 设置属性值。如果这个属性不存在，则创建这个属性。 属性的类型由结点定义确定，如果这个属性是其他类型，则系统作尽可能的类型转换。
	 * 如果系统未定义该属性类型，则如果属性
	 * 如果转换出错，则抛出ValueFormatException，其他错误，则抛出RepositoryException.
	 * 
	 * @param name
	 *            String
	 * @param values
	 *            String[]
	 * @param type
	 *            int
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 */
	public Property setProperty(String name, String[] values, int type)
			throws PathNotFoundException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		if (values == null)
			property.setValue((String) null);
		else {
			Value[] result = new Value[values.length];
			for (int i = 0; i < values.length; i++) {
				Value value = this.getSession().getValueFactory()
						.createValue(values[i], type);
				result[i] = value;
			}
			property.setValue(result);
		}
		session.addPending(this);
		return property;

	}

	/**
	 * 设置属性值。如果这个属性不存在，则创建这个属性。 属性的类型由结点定义确定，如果这个属性是其他类型，则系统作尽可能的类型转换。
	 * 如果系统未定义该属性类型，则如果属性
	 * 如果转换出错，则抛出ValueFormatException，其他错误，则抛出RepositoryException.
	 * 
	 * @param name
	 *            String
	 * @param value
	 *            Calendar
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 */
	public Property setProperty(String name, Calendar value)
			throws PathNotFoundException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		property.setValue(value);
		session.addPending(this);
		return property;

	}

	/**
	 * 设置属性值。如果这个属性不存在，则创建这个属性。 属性的类型由结点定义确定，如果这个属性是其他类型，则系统作尽可能的类型转换。
	 * 如果系统未定义该属性类型，则如果属性
	 * 如果转换出错，则抛出ValueFormatException，其他错误，则抛出RepositoryException.
	 * 
	 * @param name
	 *            String
	 * @param value
	 *            String
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 */
	public Property setProperty(String name, String value)
			throws PathNotFoundException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		property.setValue(value);
		session.addPending(this);
		return property;
	}

	/**
	 * 设置属性值。如果这个属性不存在，则创建这个属性。 属性的类型由结点定义确定，如果这个属性是其他类型，则系统作尽可能的类型转换。
	 * 如果系统未定义该属性类型，则如果属性
	 * 如果转换出错，则抛出ValueFormatException，其他错误，则抛出RepositoryException.
	 * 
	 * @param name
	 *            String
	 * @param value
	 *            InputStream
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 */
	@SuppressWarnings("deprecation")
	public Property setProperty(String name, InputStream value)
			throws PathNotFoundException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		property.setValue(value);
		session.addPending(this);
		return property;

	}

	/**
	 * 设置属性值。如果这个属性不存在，则创建这个属性。 属性的类型由结点定义确定，如果这个属性是其他类型，则系统作尽可能的类型转换。
	 * 如果系统未定义该属性类型，则如果属性
	 * 如果转换出错，则抛出ValueFormatException，其他错误，则抛出RepositoryException.
	 * 
	 * @param name
	 *            String
	 * @param value
	 *            boolean
	 * @return Property
	 * @throws RepositoryException
	 * @throws PathNotFoundException
	 */
	public Property setProperty(String name, boolean value)
			throws PathNotFoundException, RepositoryException {
		Property property = this.getProperty(name);
		if (property == null)
			throw new IllegalArgumentException("Unknown property name: " + name
					+ ".");
		property.setValue(value);
		session.addPending(this);
		return property;

	}

	@Override
	public void orderBefore(String srcChildRelPath, String destChildRelPath)
			throws UnsupportedRepositoryOperationException, VersionException,
			ConstraintViolationException, ItemNotFoundException, LockException,
			RepositoryException {
		throw new UnsupportedOperationException();

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jcr.Node#getNodes(java.lang.String[])
	 */
	@Override
	public NodeIterator getNodes(String[] nameGlobs) throws RepositoryException {
		throw new UnsupportedOperationException();
	}

	@Override
	public PropertyIterator getReferences(String name)
			throws RepositoryException {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean hasNodes() throws RepositoryException {
		NodeTableCountDao dao = session.getDao(NodeTableCountDao.class);
		try {
			return dao.countByParent(getID()) > 0;
		} catch (SQLException e) {
			throw new RepositoryException(e);
		}

	}

	@Override
	public boolean hasProperties() throws RepositoryException {
		return this.properties.size() > 0;
	}

	@Override
	public Session getSession() throws RepositoryException {
		return this.session;
	}

	@Override
	public boolean isNew() {
		return this.data.isNew();
	}

	@Override
	public boolean isModified() {
		return this.modified;
	}

	@Override
	public boolean isSame(Item otherItem) throws RepositoryException {
		if (!otherItem.isNode())
			return false;
		Node node = (Node) otherItem;
		return node.getIdentifier().equals(this.getIdentifier());
	}

	@Override
	public void accept(ItemVisitor visitor) throws RepositoryException {
		throw new UnsupportedOperationException();

	}

	@Override
	public void refresh(boolean keepChanges) throws InvalidItemStateException,
			RepositoryException {
		throw new UnsupportedOperationException();
	}

	@Override
	public String toString() {
		try {
			return "Node:{path: " + this.getPath() + "; identifier:"
					+ this.getIdentifier() + "}";
		} catch (RepositoryException e) {
			return super.toString();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jcr.Item#remove()
	 */
	@Override
	public void remove() throws VersionException, LockException,
			ConstraintViolationException, AccessDeniedException,
			RepositoryException {

		BasicObservationManager manager = (BasicObservationManager) session
				.getWorkspace().getObservationManager();
		manager.fire(BasicNode.this, Event.NODE_REMOVED);
		NodeTableDeleteDao dao = session.getDao(NodeTableDeleteDao.class);
		try {
			dao.delete(getIdentifier());
		} catch (SQLException e) {
			throw new RepositoryException(e);
		}

	}

}
